package com.googlecode.rjm.model;

import java.util.Date;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;

import com.googlecode.rjm.model.journal.IssueJournal;
import com.googlecode.rjm.model.journal.JournalDetail;
import com.googlecode.rjm.model.journal.Journalized;
import com.googlecode.rjm.model.journal.StatusJournalDetail;


@Entity
@Table(name="issues")
public class Issue extends Persistent implements Journalized<IssueJournal> {
	
	@Id
	private Integer id;
	
	@ManyToOne
	@JoinColumn(name="project_id")
	private Project project;
	
	@ManyToOne
	@JoinColumn(name="tracker_id")
	private Tracker tracker;
	
	@ManyToOne
	@JoinColumn(name="status_id")
	private Status status;
	
	@ManyToOne
	@JoinColumn(name="author_id")
	private User author;
	
	@ManyToOne
	@JoinColumn(name="parent_id")
	private Issue parent;
	
	@ManyToOne
	@JoinColumn(name="root_id")
	private Issue root;
	
	private String subject;
	
	private String description;
	
	@Column(name="start_date")
	private Date startDate;
	
	@Column(name="due_date")
	private Date dueDate;
	
	@Column(name="done_ratio")
	private Integer doneRatio;
	
	@Column(name="estimated_hours")
	private Double estimatedHours;
	
	@OneToMany(mappedBy = "issue")
	@OrderBy(value="creationDate")
	private List<IssueJournal> journals;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public Project getProject() {
		return project;
	}

	public void setProject(Project project) {
		this.project = project;
	}

	public Tracker getTracker() {
		return tracker;
	}

	public void setTracker(Tracker tracker) {
		this.tracker = tracker;
	}

	public Status getStatus() {
		return status;
	}

	public void setStatus(Status status) {
		this.status = status;
	}

	public User getAuthor() {
		return author;
	}

	public void setAuthor(User author) {
		this.author = author;
	}

	public Issue getParent() {
		return parent;
	}

	public void setParent(Issue parent) {
		this.parent = parent;
	}

	public Issue getRoot() {
		return this.root;
	}

	public void setRoot(Issue root) {
		this.root = root;
	}

	public String getSubject() {
		return subject;
	}

	public void setSubject(String subject) {
		this.subject = subject;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public Date getStartDate() {
		return startDate;
	}

	public void setStartDate(Date startDate) {
		this.startDate = startDate;
	}

	public Date getDueDate() {
		return dueDate;
	}

	public void setDueDate(Date dueDate) {
		this.dueDate = dueDate;
	}

	public Integer getDoneRatio() {
		return doneRatio;
	}

	public void setDoneRatio(Integer doneRatio) {
		this.doneRatio = doneRatio;
	}

	public Double getEstimatedHours() {
		return estimatedHours;
	}

	public void setEstimatedHours(Double estimatedHours) {
		this.estimatedHours = estimatedHours;
	}
	
	public List<IssueJournal> getJournals() {
		return this.journals;
	}

	public void setJournals(List<IssueJournal> journals) {
		this.journals = journals;
	}

	/**
	 * Look up in the issue's journals the date of the last transition to a given status.
	 * 
	 * @param s
	 *            The status to be looked.
	 * @return The last transition date to this status.
	 */
	public Date getLastTransitionForStatus(Status s) {
		return getTransitionForStatus(s, false);
	}
	
	/**
	 * Look up in the issue's journals the date of the first transition to a given status.
	 * 
	 * @param s
	 *            The status to be looked.
	 * @return The first transition date to this status.
	 */
	public Date getFirstTransitionForStatus(Status s) {
		return getTransitionForStatus(s, true);
	}
	
	protected Date getTransitionForStatus(Status s, boolean first) {
		Date ret = null;
		
		outter:
		for (IssueJournal issueJournal : this.getJournals()) {
			for (@SuppressWarnings("unchecked") JournalDetail journalDetail : issueJournal.getDetails()) {
				if (journalDetail instanceof StatusJournalDetail) {
					StatusJournalDetail statusJournalDetais = (StatusJournalDetail) journalDetail;
					if (statusJournalDetais.getTypedValue().equals(s)) {
						ret = issueJournal.getCreationDate();
						if (first) {
							break outter;
						}
					}
				}
			}
		}
		return ret;
	}
	
	/**
	 * Test if the <code>this</code> issue is under other in the tree.
	 * 
	 * @param possibleParent
	 *            The issue being tested as parent.
	 * @return <code>true</code> if this issue is directly or indirectly child of other, <code>false</code> otherwise.
	 */
	public boolean isUnder(Issue possibleParent) {
		if (! this.getRoot().equals(possibleParent.getRoot())) return false;
		
		Issue current = this;
		while (! current.equals(current.getRoot())) {
			if (current.getParent().equals(possibleParent)) {
				return true;
			}
			current = current.getParent();
		}
		return false;
	}
	
	public int distanceToRoot() {
		int dist = 0;
		Issue current = this;
		while (! current.equals(current.getRoot())) {
			current = current.getParent();
			dist++;
		}
		return dist;
	}

}
